﻿#include "trim.h"

TopoDS_Shape generate_trimmed_face(const std::vector<std::shared_ptr<xi::geom::Geom_BSplineCurve<3>>> &curves,
                                   std::shared_ptr<xi::geom::Geom_BSplineSurface<3>> surface)
{
    xi::utils::Adaptor_OCCBSplineSurface<3> adaptor = surface.get();
    Handle(Geom_BSplineSurface) sur = adaptor;
    if (curves.size() <= 4)
        return BRepBuilderAPI_MakeFace(sur, 1e-12);

    std::vector<Handle(Geom_BSplineCurve)> hcurves;
    std::vector<double> firsts, seconds;
    for (auto &curve : curves)
    {
        xi::utils::Adaptor_OCCBSplineCurve<3> adp = curve.get();
        hcurves.push_back(adp);
        firsts.push_back(curve->first_param());
        seconds.push_back(curve->last_param());
    }

    // 获得采样点
    int N = 1000;
    TColgp_Array1OfPnt points(1, hcurves.size() * N);
    for (int i = 0; i < hcurves.size(); i++)
        for (int j = 0; j < N; j++)
            points.SetValue(i * N + j + 1, hcurves[i]->Value(firsts[i] + (seconds[i] - firsts[i]) / (N - 1) * j));

    double U1, U2, V1, V2;
    sur->Bounds(U1, U2, V1, V2);

    std::vector<TopoDS_Edge> anEdges;
    for (int i = 0; i < hcurves.size(); i++)
    {
        // 计算边界 2D 曲线
        Handle(TColgp_HArray1OfPnt2d) ppoints = new TColgp_HArray1OfPnt2d(1, N);
        Handle(TColStd_HArray1OfReal) params = new TColStd_HArray1OfReal(1, N);

        double t = 0;
        for (int j = 0; j < N; j++)
        {
            double U = (U1 + U2) / 2, V = (V1 + V2) / 2;
            auto P = points.Value(i * N + j + 1);
            xi::utils::Adaptor_OCCVec<3> adp(P);
            auto uv = xi::alg::Alg_Projection<3>::torus(surface, adp, U, V, 1e-6);

            ppoints->SetValue(j + 1, gp_Pnt2d(uv.x(), uv.y()));
            params->SetValue(j + 1, t);
            t += 1.0 / (N - 1);
        }

        auto interp = Geom2dAPI_Interpolate(ppoints, params, false, 1e-16);
        interp.Perform();
        Handle(Geom2d_Curve) boundary = interp.Curve();

        int cnt = 2;
        int step = N / cnt;
        auto start = params->Value(1);
        for (int j = 0; j < cnt; j++)
        {
            double end = params->Value(step * (j + 1));
            anEdges.push_back(BRepBuilderAPI_MakeEdge(boundary, sur, start, end));
            start = end;
        }
    }

    // 将边界按顺序连接
    ShapeAnalysis_Edge anEdgeAnalyser;
    ShapeAnalysis_WireOrder aWireOrder;
    aWireOrder.SetCouples(1e-4);
    for (std::vector<TopoDS_Edge>::const_iterator i = anEdges.begin(); i != anEdges.end(); ++i)
    {
        TopoDS_Vertex aVf = anEdgeAnalyser.FirstVertex(*i);
        TopoDS_Vertex aVl = anEdgeAnalyser.LastVertex(*i);

        gp_Pnt aPf = BRep_Tool::Pnt(aVf);
        gp_Pnt aPl = BRep_Tool::Pnt(aVl);

        aWireOrder.Add(aPf.XYZ(), aPl.XYZ());
    }

    TopTools_ListOfShape aOrderedEdges;
    for (Standard_Integer e = 1; e <= aWireOrder.NbEdges(); ++e)
    {
        const TopoDS_Edge &anEdge = anEdges[e - 1];
        aOrderedEdges.Append(anEdge);
    }

    BRepBuilderAPI_MakeWire bdWire;
    bdWire.Add(aOrderedEdges);

    if (bdWire.IsDone())
    {
        auto face = BRepBuilderAPI_MakeFace(sur, bdWire.Wire());
        write_stp({face}, "tmp.stp");
        return read_stp("tmp.stp")[0];
    }
    else
        return BRepBuilderAPI_MakeFace(sur, 1e-12);
}

std::vector<TopoDS_Shape> read_stp(const char *path)
{
    // 创建读取器
    STEPControl_Reader reader;
    reader.ReadFile(Standard_CString(path));

    // 检查读取信息
    reader.PrintCheckLoad(Standard_True, IFSelect_ItemsByEntity);

    // 获得可翻译的根实体数量，然后翻译所有实体，获得成功的数量
    Standard_Integer NbRoots = reader.NbRootsForTransfer();
    Standard_Integer NbTrans = reader.TransferRoots();

    // 检查翻译信息
    reader.PrintCheckTransfer(Standard_True, IFSelect_ItemsByEntity);

    // 添加获得的形状
    std::vector<TopoDS_Shape> shapes;
    for (int i = 1; i <= reader.NbShapes(); i++)
    {
        auto shape = reader.Shape(i);
        shapes.push_back(shape);
    }

    return shapes;
}

void write_stp(const std::vector<TopoDS_Shape> &shapes, const char *path)
{
    // 创建写入器
    STEPControl_Writer writer;

    // 翻译所有形状
    for (auto sh : shapes)
        writer.Transfer(sh, STEPControl_AsIs);

    // 写入文件
    writer.Write(Standard_CString(path));
}